package com.example.demo_mysql_mybatisplus.util.api.crudFree;


import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.demo_mysql_mybatisplus.util.controller.advicer.ErrorEnum;
import com.example.demo_mysql_mybatisplus.util.controller.advicer.core.RestException;
import com.example.demo_mysql_mybatisplus.util.api.crudFree.dto.FreeEditDto;
import com.example.demo_mysql_mybatisplus.util.api.crudFree.dto.FreeQueryDto;
import com.example.demo_mysql_mybatisplus.util.grouped.GroupedPo;
import com.example.demo_mysql_mybatisplus.util.grouped.Grouper;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;

/*
 * https://mp.weixin.qq.com/s/60_RFe3uFRo9SZfx580mBQ
 * */
public interface FreeService<T> extends IService<T> {

    /************************************ 1/3 定制函数 ************************************/
    /**
     * （数据库大小写敏感时需要特殊处理） `unique_key` varchar(128) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '应用内唯一KEY',
     * 存的时候要大小写敏感，模糊查询的时候不要大小写敏感
     * */
    default List<String> queryIgnoreCaseFields(){
        return Collections.emptyList();// new LinkedList<String>().stream().map(String::toLowerCase).collect(Collectors.toList());
    }
    default void beforeAddOne(T entity){ }
    default void beforeEditOne(T entity){ }
    default void beforeChangeFieldValueByIds(FreeEditDto reqDto){ }
    default void beforeDeleteByIds(Set<Long> ids){ }
    /** queryByConditions 时，额外的查询条件 */
    default void extraCondition(FreeQueryDto reqDto, QueryWrapper<T> qWrapper){ }
    /** queryByConditions 时，权限条件限制 */
    default void dealAuthLimit(FreeQueryDto reqDto, QueryWrapper<T> qWrapper){
//        if (CollUtil.contains(reqDto.getAuthLimitCodes(), "authCode1")) {
//            qWrapper.eq("status", 0).eq("is_delete", 0);
//        }
    }
    /** queryByConditions 时，填充/处理结果 */
    default List fillResults(FreeQueryDto reqDto,List<T> records){
        return records;
    }

    /************************************ 2/3 开放函数 ************************************/

    /** 批量新增数据 */
    default T addOne(T entity) {
        beforeAddOne(entity);
        this.save(entity);
        return entity;
    }

    /** 批量编辑数据 */
    default void editOne(T entity) {
        beforeEditOne(entity);
        this.updateById(entity);
    }

    /** 根据ID修改某个字段的值 */
    default void changeFieldValueByIds(FreeEditDto reqDto) {
        beforeChangeFieldValueByIds( reqDto);
        String dbField = getDbField(classOfT(), reqDto.getFieldName());
        this.update(new UpdateWrapper<T>().in("id", reqDto.getIds()).set(dbField, reqDto.getFieldValue())
        );
    }

    /** 根据ID删除数据 */
    default void deleteByIds( Set<Long> ids) {
        beforeDeleteByIds( ids);
        this.removeByIds(ids);
    }

    /** 自由条件查询
     * 返回结果根据条件可能为List或Page或Map
     * */
    default QueryWrapper<T> buildQueryWrapper(FreeQueryDto reqDto) {
        QueryWrapper<T> qWrapper = Wrappers.query();
        Class clasz = classOfT();
        //// 1、精确条件
        if (CollUtil.isNotEmpty(reqDto.getEqualCondition())) {
            for (Map.Entry<String, Object> entry : reqDto.getEqualCondition().entrySet()) {
                String dbField = getDbField(clasz, entry.getKey());
                if (entry.getValue() instanceof List ) {
                    List list = (List) entry.getValue();
                    qWrapper.in( CollUtil.isNotEmpty(list), dbField, list);
                } else {
                    qWrapper.eq(ObjectUtil.isNotEmpty(entry.getValue()),dbField, entry.getValue());
                }
            }
        }
        if (CollUtil.isNotEmpty(reqDto.getNotEqualCondition())) {
            for (Map.Entry<String, List<Object>> entry : reqDto.getNotEqualCondition().entrySet()) {
                String dbField = getDbField(clasz, entry.getKey());
                qWrapper.notIn(CollUtil.isNotEmpty(entry.getValue()), dbField, entry.getValue());
            }
        }
        //// 2、范围条件
        if (CollUtil.isNotEmpty(reqDto.getRangeCondition())) {

        }
        //// 3、模糊条件
        if (CollUtil.isNotEmpty(reqDto.getLikeCondition())) {
            int counter = 0;
            StringBuffer likeCondition = new StringBuffer("(");
            for (Map.Entry<String, String> entry : reqDto.getLikeCondition().entrySet()) {
                String dbField = getDbField(clasz, entry.getKey());
                String tmpValue = entry.getValue();
                if( StrUtil.isNotEmpty(tmpValue) ){
                    tmpValue = FreeUtil.replaceSpecialCharacter(tmpValue); // 特殊字符转义
                    if(counter++ >0) likeCondition.append(" OR ");
                    likeCondition.append( dbField );
                    if(queryIgnoreCaseFields().contains(dbField.toLowerCase())) likeCondition.append( " COLLATE utf8_general_ci" );
                    likeCondition.append(" like '%").append( tmpValue ).append("%' ");
                }
            }
            qWrapper.apply(counter>0, likeCondition.append(")").toString() );
        }
        //// 4、额外条件
        extraCondition(reqDto, qWrapper);
        //// 5、权限限制
        dealAuthLimit(reqDto, qWrapper);

        return qWrapper;
    }

    /**
     * 识别查询方式，执行查询，填充数据，分组
     */
    default Object doQuery( FreeQueryDto reqDto,QueryWrapper<T> qWrapper) {
        if( ( StrUtil.isNotEmpty(reqDto.getBigGroupField()) && ( CollUtil.isEmpty(reqDto.getOrderBy()) || !(reqDto.getOrderBy().containsKey(reqDto.getBigGroupField()) ) ) )
                || ( StrUtil.isNotEmpty(reqDto.getSmallGroupField()) && ( CollUtil.isEmpty(reqDto.getOrderBy()) || !(reqDto.getOrderBy().containsKey(reqDto.getSmallGroupField()) ) ) )
        ){
            ErrorEnum.PARAM_ERROR.throwEx("分组需要按照分组字段排序，且分组字段必须放在第一排序位");
        }

        Class clasz = classOfT();
        if (reqDto.getPageNum() == null && reqDto.getPageSize() == null) {
            dealOrderBy( clasz, reqDto,qWrapper);//// 排序方式
            dealReturnFields( clasz, reqDto, qWrapper);//// 指定要返回的字段
            List list = this.list(qWrapper);//// 执行查询
            list = fillResults(reqDto,list);//// 填充数据
            if ( StrUtil.isNotEmpty(reqDto.getSmallGroupField()) ) {
                return groupResults(list, reqDto.getSmallGroupField() );// list内分组，返回Map
            }else if( StrUtil.isNotEmpty(reqDto.getBigGroupField()) ){
                return groupResults(list, reqDto.getBigGroupField() );// list内分组，返回Map
            }else{
                return list;// 常规list，返回List
            }
        } else if (reqDto.getPageNum() != null && reqDto.getPageSize() != null) {
            if ( StrUtil.isEmpty(reqDto.getBigGroupField())) {
                dealOrderBy( clasz, reqDto,qWrapper);//// 排序方式
                dealReturnFields( clasz, reqDto, qWrapper);//// 指定要返回的字段
                Page page = this.page(new Page<>(reqDto.getPageNum(), reqDto.getPageSize()), qWrapper);//// 执行查询
                List list = fillResults(reqDto,page.getRecords());//// 填充数据
                if ( StrUtil.isEmpty(reqDto.getSmallGroupField())) {
                    page.setRecords( list ); // 常规page，返回page
                }else{
                    //// 数据小分组
                    Map groupedData = groupResults(list, reqDto.getSmallGroupField());
                    page.setRecords(Arrays.asList(groupedData));// 页内分组，返回page
                }
                return page;
            }else{
                //// 数据大分组
                String groupFieldInDb = StrUtil.toUnderlineCase(reqDto.getBigGroupField());
                dealOrderBy( clasz, reqDto,qWrapper);//// 排序方式

                qWrapper.select(" DISTINCT " + groupFieldInDb +" AS " + reqDto.getBigGroupField() );//// 指定要返回的字段
                Page page0 = this.page(new Page<>(reqDto.getPageNum(), reqDto.getPageSize()), qWrapper);//// 执行查询
                List list0 = page0.getRecords();
                if(list0.size()!=0){
                    qWrapper.in( groupFieldInDb , list0.stream().map(item -> BeanUtil.getFieldValue(item, reqDto.getBigGroupField() )).toArray() );
                    dealReturnFields( clasz, reqDto, qWrapper);//// 指定要返回的字段
                    List list = this.list(qWrapper);//// 执行查询
                    list = fillResults(reqDto,list);//// 填充数据
                    Map groupedData = groupResults(list, reqDto.getBigGroupField());

                    page0.setRecords(Arrays.asList(groupedData));// 按组分页，返回page
                }
                return page0;
            }
        } else {
            ErrorEnum.PARAM_ERROR.throwEx("分页参数不正确");
        }
        return null;
    }

    /************************************ 3/3工具函数 ************************************/

    /**
     * 对数据分组
     * @see Grouper
     */
    default <U> Map<Object, GroupedPo> groupResults(List<U> datas, String groupField ) {
        if (CollUtil.isEmpty(datas) ) return new HashMap(0);
        // 生成分组(保持顺序)
        Map<Object, GroupedPo> groupedDataMap = new LinkedHashMap();
        //// 初始组 以及Key值
        for (U data : datas) {
            Object groupFieldVal = BeanUtil.getFieldValue(data, groupField);// 不是对象 需要转成json获取值
            if (!groupedDataMap.containsKey(groupFieldVal)) {
                groupedDataMap.put(groupFieldVal, new GroupedPo(groupField,groupFieldVal));
            }
        }
        //// 按组填充数据
        for (U data : datas) {
            Object groupFieldVal = BeanUtil.getFieldValue(data, groupField);// 不是对象 需要转成json获取值
            groupedDataMap.get(groupFieldVal).getDatas().add(data);
        }
        return groupedDataMap;
    }

    /**
     * 指定要返回的字段
     */
    default void dealReturnFields(Class clasz, FreeQueryDto reqDto,QueryWrapper<T> qWrapper){
        if (ArrayUtil.isNotEmpty(reqDto.getSpecialReturnFields())) {
            for (int i = 0; i < reqDto.getSpecialReturnFields().length; i++) {
                String dbField = this.getDbField(clasz, reqDto.getSpecialReturnFields()[i]);
                reqDto.getSpecialReturnFields()[i] = dbField;
            }
            qWrapper.select(reqDto.getSpecialReturnFields());
        }
    }

    /**
     * 排序方式
     */
    default void dealOrderBy(Class clasz, FreeQueryDto reqDto,QueryWrapper<T> qWrapper){
        if (CollUtil.isNotEmpty(reqDto.getOrderBy())) {
            reqDto.getOrderBy().entrySet().stream().forEach(entry -> {
                String dbField = getDbField(clasz, entry.getKey());
                if (entry.getValue().equalsIgnoreCase("desc")) {
                    qWrapper.orderByDesc(dbField);
                } else {
                    qWrapper.orderByAsc(dbField);
                }
            });
        }
    }

    /**
     * 判断实例字段是否存在；
     * 获取数据库字段
     */
    default String getDbField(Class clasz, String fieldName) {
        if (!ReflectUtil.hasField(clasz, fieldName)) {
            if(StrUtil.contains(fieldName,"_")){
                return fieldName;
            }
            ErrorEnum.PARAMETERS_NOT_EXISTS.throwEx("指定的字段不存在：" + fieldName);
        }
        String dbField=null;
        if (clasz.isAnnotationPresent(TableField.class)) {
            dbField = AnnotationUtil.getAnnotation(clasz, TableField.class).value();
        }
        if(StrUtil.isEmpty(dbField)) {
            dbField = StrUtil.toUnderlineCase(fieldName);
        }
        return dbField;
    }

    /**
     * 获取泛型类
     */
    default Class classOfT() {
        Type[] arrs = getClass().getGenericInterfaces();
        for (Type arr : arrs) {
            if (arr instanceof ParameterizedType) {
                ParameterizedType pt = (ParameterizedType) arr;
                if (pt.getRawType() == FreeService.class) {
                    return (Class<T>) pt.getActualTypeArguments()[0];
                }
            }
        }
        throw new RestException(ErrorEnum.SYSTEM_ERROR.getCode(), "接口实现存在问题，请检查代码是否指定泛型" );
    }

}

